// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © dg_factor

//@version=6
indicator("Anchored Grids ft. Volume", overlay=true)

// Inputs
tf        = input.timeframe("1D", "TimeFrame", tooltip=
 "Select a higher timeframe for grid calculation. The grid levels will be calculated based on this timeframe's open/high/low values. Please ensure that your chart includes the starting bar of the selected timeframe to accurately calculate the levels.")
mod       = input.string("Geometric", "Mode", ["Arithmetic", "Geometric"], tooltip="Arithmetic: Equal price between levels\nGeometric: Equal percentage between levels.")
grids     = input.int(10, "Grids", minval=2, tooltip="Number of grid levels to draw between highest and lowest prices.")
col       = input.color(#2962ff, "Color", tooltip="Base color for grid lines and labels. When volume data is displayed, lower values are darkened by 50%.")
sh_volume = input.bool(true, "Show Volume Accumulation", tooltip="Display the effect of volume data on the width of lines and the data value on labels.")
tolerance = input.float(0.1, "Tolerance (%)", step=0.01, tooltip="Percentage range around each level to register volume touches.")

// Variables
var int     x1 = na
var float   y1 = na
var float   hi = na
var float   lo = na
var line[]  lns = array.new_line(0)
var label[] lbs = array.new_label(0)
var float[] level_volumes = array.new<float>(0)

// Calcs
anc = ta.change(time(tf)) != 0
if anc
    x1 := time
    y1 := open
    array.clear(level_volumes)
    for i = 0 to grids - 1 
        array.push(level_volumes, 0.0)
if anc or high > hi
    hi := high
if anc or low < lo
    lo := low
diff = (hi - lo) / (grids - 1)
rate = math.pow((hi / lo), (1. / (grids - 1)))

// Levels & Volume Data
if not na(hi) and not na(lo) and hi > lo
    if array.size(level_volumes) == 0
        for i = 0 to grids - 1
            array.push(level_volumes, 0.0)    
    for i = 0 to grids - 1
        art = lo + diff * i
        geo = lo * math.pow(rate, i)
        level_price = mod == "Arithmetic" ? art : geo
        tolerance_range = level_price * tolerance / 100
        if low <= level_price + tolerance_range and high >= level_price - tolerance_range
            current_vol = array.get(level_volumes, i)
            array.set(level_volumes, i, current_vol + volume)
if barstate.islast
    if array.size(lns) > 0
        for i = 0 to array.size(lns) - 1
            line.delete(array.get(lns, i))
            label.delete(array.get(lbs, i))
        array.clear(lns)
        array.clear(lbs)
    if not na(hi) and not na(lo) and hi > lo
        max_volume = 0.0
        if array.size(level_volumes) > 0
            for i = 0 to array.size(level_volumes) - 1
                vol = array.get(level_volumes, i)
                if vol > max_volume
                    max_volume := vol        
        for i = 0 to grids - 1
            art = lo + diff * i
            geo = lo * math.pow(rate, i)
            lvl = mod == "Arithmetic" ? art : geo
            per = (lvl - y1) / y1 * 100
            level_vol = 0.0
            if i < array.size(level_volumes)
                level_vol := array.get(level_volumes, i)
            line_width = 1
            line_color = col            
            if sh_volume and max_volume > 0 and level_vol > 0
                volume_ratio = level_vol / max_volume
                light_color = color.new(col, 50)
                dark_color = color.new(col, 0)  
                line_color := color.from_gradient(volume_ratio, 0, 1, light_color, dark_color)
                line_width := 1 + int(volume_ratio * 3)
            ln = line.new(x1, lvl, time, lvl, xloc.bar_time, extend.none, line_color, line.style_solid, line_width)
            label_text = "[" + str.tostring(i+1) + "]  ($ " + str.tostring(lvl, format.mintick) + ")" + "  (" + str.tostring(per, "#.##") + "%)"
            if sh_volume
                volume_text = level_vol >= 1000000 ? str.tostring(level_vol/1000000, "#.##") + "M" : 
                             level_vol >= 1000 ? str.tostring(level_vol/1000, "#.##") + "K" : 
                             str.tostring(level_vol, "#")
                label_text += "  (Vol: " + volume_text + ")"
            lb = label.new(time, lvl, label_text, xloc.bar_time, yloc.price, #00000000, label.style_label_left, line_color, size.normal, text.align_right)            
            array.push(lns, ln)
            array.push(lbs, lb)
//

plotshape(anc, "Anchored TF Start ⚓", shape.labeldown, location.top, #00000000, text="⚓", precision=0)

// Bitti :)
plotshape(barstate.isfirst, "@ dg_factor", shape.flag, location.bottom, #00000000, precision=0, editable=false)